using System;
using Tycho.Grammar;
using Tycho.Parser.Peg;

namespace Tycho.Language {
    public class ParserLoader : IParserLoader {
        private readonly Func<IProductionCompiler> CreateGrammarCompiler;
        private readonly IDynamicParserCreator DynamicParserCreator;
        private readonly Func<IDynamicGrammar> CreateDynamicGrammar;
        private readonly IGrammarParser GrammarParser;
        private readonly Func<IProductionLookup> CreateProductionLookup;

        public ParserLoader () : this (new GrammarParser (), CreateDefaultProductionLookup, () => new GrammarCompiler (), new DynamicParserCreator (), () => new LazyDynamicGrammar ()) {
        }

        public ParserLoader (IGrammarParser grammarParser, Func<IProductionLookup> createProductionLookup, Func<IProductionCompiler> createGrammarCompiler, IDynamicParserCreator createDynamicParser, Func<IDynamicGrammar> createDynamicGrammar) {
            GrammarParser = grammarParser;
            CreateProductionLookup = createProductionLookup;
            CreateGrammarCompiler = createGrammarCompiler;
            DynamicParserCreator = createDynamicParser;
            CreateDynamicGrammar = createDynamicGrammar;
        }

        public IParser LoadParser (string source) {
            ITerm grammarTerm = GrammarParser.LoadGrammarTerms(source);

            var environment = new IdentifierProductionCompilerEvaluator (IntegerProduction.CreateTerminal ());
            IProductionCompiler grammarCompiler = CreateGrammarCompiler ();
            IProductionLookup productionLookup = CreateProductionLookup ();
            IDynamicGrammar dynamicGrammar = CreateDynamicGrammar ();

            IProduction production = grammarCompiler.Compile (grammarTerm, environment, productionLookup, dynamicGrammar).Production;

            return DynamicParserCreator.CreateDynamicParser (dynamicGrammar, production);
        }

        private static IProductionLookup CreateDefaultProductionLookup () {
            var productionLookup = new ProductionLookup ();

            productionLookup.Add ("id", IdentifierProduction.CreateTerminal ());
            productionLookup.Add ("module-id", TychoIdentifierFactory.CreateTychoIdentifierProduction ());
            productionLookup.Add ("int", IntegerProduction.CreateTerminal ());
            productionLookup.Add ("float", FloatingPointProduction.CreateTerminal ());

            return productionLookup;
        }
    }
}
